Fixed handling unknown statements with result set#2785
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Client V2 CoverageCoverage Report
Class Coverage
|
JDBC V2 CoverageCoverage Report
Class Coverage
|
JDBC V1 CoverageCoverage Report
Class Coverage
|
Client V1 CoverageCoverage Report
Class Coverage
|
There was a problem hiding this comment.
Pull request overview
This PR addresses JDBC v2 behavior when the SQL parser classifies a statement as UNKNOWN, ensuring Statement.execute(...) / PreparedStatement.execute() can still expose a ResultSet when the server returns one (e.g., for syntactically odd but valid SELECTs).
Changes:
- Treat
UNKNOWN(and parse-error cases for ANTLR) as potentially result-set producing inSqlParserFacade. - Adjust
StatementImpl/PreparedStatementImplexecution behavior to returnfalsewhen noResultSetis produced and to throw fromexecuteQuery(...)when noResultSetexists. - Add integration tests covering an “unknown” SELECT that still returns a
ResultSetacross SQL parser implementations.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java |
Marks UNKNOWN / parse-error statements as result-set capable for parser-driven execution routing. |
jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java |
Updates executeQuery(...)/execute(...) to reflect presence/absence of a ResultSet based on schema detection. |
jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java |
Aligns executeQuery() with StatementImpl behavior and adjusts execute() return value based on actual ResultSet. |
jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java |
Adds integration test verifying execute(...) returns a ResultSet for an “UNKNOWN” SELECT across parsers. |
jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java |
Adds integration test verifying PreparedStatement.execute() returns a ResultSet for an “UNKNOWN” SELECT. |
client-v2/src/test/java/com/clickhouse/client/datatypes/DataTypeTests.java |
File-wide normalization/rewriting (appears formatting/EOL-only in this diff). |
Comments suppressed due to low confidence (1)
jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java:302
PreparedStatementImpl.execute()does not resetcurrentUpdateCount/currentResultSetbefore execution. With the newreturn currentResultSet != nullbehavior, a no-schema response (null ResultSet) will returnfalsebutgetUpdateCount()may still report a stale value from a prior execution (and for successful queries it may also remain non-(-1)). Align this withStatementImpl.execute(...)by resetting statement state at the start ofexecute().
public boolean execute() throws SQLException {
ensureOpen();
if (parsedPreparedStatement.isHasResultSet()) {
currentResultSet = super.executeQueryImpl(buildSQL(), localSettings);
return currentResultSet != null;
} else {
currentUpdateCount = super.executeUpdateImpl(buildSQL(), localSettings);
return false;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
This PR updates JDBC v2 statement execution behavior to better handle SQL that the client-side parser classifies as UNKNOWN/errored but ClickHouse can still execute successfully, ensuring execute()/executeQuery() comply with JDBC ResultSet/update-count semantics.
Changes:
- Treat
UNKNOWN(JavaCC) and parse-error (ANTLR) statements as “result-set capable” soexecute()can still expose aResultSetwhen the server returns one. - Adjust
Statement.execute(...)/PreparedStatement.execute()to returnfalsewhen no schema/result set is returned, and makeexecuteQuery(...)throw when noResultSetis produced. - Add integration tests covering the “unknown SELECT still returns a ResultSet” scenario across parser implementations.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java | Marks UNKNOWN / parse-error statements as result-set capable to avoid prematurely treating them as updates. |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java | Tightens JDBC semantics: executeQuery throws without a ResultSet, execute returns false when no result set is produced, and update count is best-effort derived when schema is missing. |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java | Aligns PreparedStatement behavior with Statement changes (executeQuery and execute semantics). |
| jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java | Adds integration coverage for UNKNOWN/parse-error statements across SQL parsers. |
| jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java | Adds integration coverage for UNKNOWN/parse-error SELECT via PreparedStatement. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
This PR adjusts JDBC v2 execution semantics for statements that the SQL parser classifies as UNKNOWN/parse-error, so Statement.execute(...) / PreparedStatement.execute() can still expose a ResultSet when the server returns one, and correctly report “no result set” otherwise (per JDBC expectations).
Changes:
- Treat
UNKNOWN(and ANTLR parse-error cases) as potentially result-set-producing during SQL parsing. - Make
execute()returnfalsewhen the execution path yields no schema/ResultSet, and makeexecuteQuery(...)throw when noResultSetis produced. - Add integration tests covering UNKNOWN/parse-error
SELECTand no-ResultSetcases for bothStatementandPreparedStatement.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java | Adds integration coverage for UNKNOWN/parse-error routing and JDBC boolean/exception contracts. |
| jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java | Adds integration coverage for UNKNOWN/parse-error SELECT and no-ResultSet DDL behavior. |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java | Marks UNKNOWN / parse-error statements as result-set-capable to allow response-driven determination. |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java | Makes executeQuery() throw when no ResultSet, and makes schema-null responses return null + update count for execute(). |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java | Routes executeQuery() through StatementImpl.executeQuery(...) and makes execute() return false when no ResultSet. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
This PR adjusts JDBC v2 execution semantics for SQL statements that the client-side parser classifies as UNKNOWN (or encounters parse errors), so execute(...)/execute() can still surface a ResultSet when the server returns one, while keeping JDBC behavior correct when no ResultSet is produced.
Changes:
- Treat
UNKNOWNstatements (and ANTLR parse-error cases) as potentially returning aResultSetinSqlParserFacade. - Tighten
Statement.executeQuery(...)/PreparedStatement.executeQuery()to throw when noResultSetis produced, and makeexecute(...)returnfalsewhen execution yields no schema. - Add integration tests covering UNKNOWN-select and no-ResultSet paths (DDL/INSERT).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SqlParserFacade.java | Marks UNKNOWN / parse-error statements as result-set capable. |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java | Updates executeQuery/execute semantics and handles schema-less responses. |
| jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java | Aligns executeQuery() and execute() behavior with new Statement semantics. |
| jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java | Adds integration test coverage for UNKNOWN statements across parsers. |
| jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java | Adds integration tests for UNKNOWN select and DDL no-ResultSet behavior. |
Comments suppressed due to low confidence (2)
jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java:303
PreparedStatementImpl.execute()resetscurrentUpdateCountbut does not clearcurrentResultSetwhen executing a statement that does not produce a ResultSet. After an update/DDL execution,getResultSet()can still return the previous ResultSet reference. SetcurrentResultSet = null(and/or close the previous ResultSet per JDBC rules) before executing, similar toStatementImpl.execute(...).
public boolean execute() throws SQLException {
ensureOpen();
currentUpdateCount = -1;
if (parsedPreparedStatement.isHasResultSet()) {
currentResultSet = super.executeQueryImpl(buildSQL(), localSettings);
return currentResultSet != null;
} else {
currentUpdateCount = super.executeUpdateImpl(buildSQL(), localSettings);
return false;
}
jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java:374
execute(String)no longer clearscurrentResultSetbefore running an update/DDL statement. If the previous execution produced a ResultSet, then callingexecute(...)with a non-ResultSet statement can leavegetResultSet()returning a stale (already-consumed) ResultSet, which violates JDBC semantics (new execution should close/replace the current ResultSet). Reset (and ideally close) the current ResultSet at the start ofexecute(...)(or at least in the non-ResultSet branch).
public boolean execute(String sql) throws SQLException {
ensureOpen();
parsedStatement = connection.getSqlParser().parsedStatement(sql);
currentUpdateCount = -1;
if (parsedStatement.isHasResultSet()) {
currentResultSet = executeQueryImpl(sql, localSettings);
return currentResultSet != null;
} else {
currentUpdateCount = executeUpdateImpl(sql, localSettings);
postUpdateActions();
return false;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|



Summary
Closes: #2784
Checklist
Delete items not relevant to your PR:
Note
Medium Risk
Changes JDBC
Statement/PreparedStatementexecution behavior around whether a query is considered to return aResultSet, which can affect client control flow and error handling for edge-case SQL parsing outcomes.Overview
Fixes handling of SQL parsed as
UNKNOWNby treating it as result-set capable in the SQL parser, soStatement.execute(...)/PreparedStatement.execute()can still expose aResultSetwhen the server returns one.Tightens JDBC execution semantics by returning
falsewhenexecuteQueryImpl(...)yields no schema/result set and by throwing inStatement.executeQuery(...)when noResultSetis produced; adds integration tests coveringUNKNOWN(malformed/odd) SELECTs and adjustsDatabaseMetaDataexpectations forUInt128to reportTypes.NUMERIC.Written by Cursor Bugbot for commit 857cbea. This will update automatically on new commits. Configure here.